//=========================================================================
// Copyright (C) 2024 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

/*  =========================================================================
    zmtp_msg - message class

    Copyright (c) contributors as noted in the AUTHORS file.
    This file is part of libzmtp, the C ZMTP stack.

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at http://mozilla.org/MPL/2.0/.
    =========================================================================
*/

#include "zmtp_classes.h"
#include "zmtp_msg.h"

CMemPool *g_zmtp_msg_buffer = NULL;

/**
 * Constructor; it allocates buffer for message data.
 * The initial content of the allocated buffer is undefined.
 */
zmtp_msg_t *zmtp_msg_new(byte flags, size_t size)
{
    if ((0 == size) || (size > MAX_ZMTP_MSG_SIZE)) {
        return NULL;
    }

    zmtp_msg_t *self = NULL;
    if (NULL != g_zmtp_msg_buffer) {
        self = (zmtp_msg_t *)CMemPool_Alloc(g_zmtp_msg_buffer,
                                     sizeof(zmtp_msg_t), MUST_USE_MEM_POOL);
    }
    if (NULL == self) {
        self = (zmtp_msg_t *)zmalloc(sizeof(zmtp_msg_t));
    }

    if (NULL == self) {
        return NULL;
    }

    self->flags = flags;
    self->data = (byte *)zmalloc(size);
    if (NULL == self->data) {
        zfree(self);
        return NULL;
    }

    self->size = size;
    self->greedy = true;
    return self;
}

/**
 * Constructor; takes ownership of data and frees it when destroying the
 * message. Nullifies the data reference.
 */
zmtp_msg_t *zmtp_msg_from_data(byte flags, byte **data_p, size_t size)
{
    if ((0 == size) || (NULL == data_p)) {
        return NULL;
    }

    zmtp_msg_t *self = NULL;
    if (NULL != g_zmtp_msg_buffer) {
        self = (zmtp_msg_t *)CMemPool_Alloc(g_zmtp_msg_buffer,
                                     sizeof(zmtp_msg_t), MUST_USE_MEM_POOL);
    }
    if (NULL == self) {
        self = (zmtp_msg_t *)zmalloc(sizeof(zmtp_msg_t));
    }

    if (NULL == self) {
        return NULL;
    }

    self->flags = flags;
    self->data = *data_p;
    self->size = size;
    self->greedy = true;
    *data_p = NULL;
    return self;
}

/**
 * Constructor that takes a constant data and does not copy, modify, or
 * free it.
 */
zmtp_msg_t *zmtp_msg_from_const_data(byte flags, void *data, size_t size)
{
    zmtp_msg_t *self = NULL;
    if (NULL != g_zmtp_msg_buffer) {
        self = (zmtp_msg_t *)CMemPool_Alloc(g_zmtp_msg_buffer,
                                     sizeof(zmtp_msg_t), MUST_USE_MEM_POOL);
    }
    if (NULL == self) {
        self = (zmtp_msg_t *)zmalloc(sizeof(zmtp_msg_t));
    }

    if (NULL == self) {
        return NULL;
    }

    self->flags = flags;
    self->data = data;
    self->size = size;
    self->greedy = false;
    return self;
}

/**
 * Destructor; frees message data and destroys the message
 */
void zmtp_msg_destroy(zmtp_msg_t **self_p)
{
    if (NULL == self_p) {
        return;
    }

    if (NULL != *self_p) {
        zmtp_msg_t *self = *self_p;
        if (self->greedy) {
            zfree(self->data);
        }
        if ((NULL != g_zmtp_msg_buffer) &&
                               (CMemPool_CheckExist(g_zmtp_msg_buffer, self))) {
            CMemPool_Free(g_zmtp_msg_buffer, self);
        }
        else {
            zfree(self);
        }

        *self_p = NULL;
    }
}

/**
 * Return message flags property
 */
byte zmtp_msg_flags(zmtp_msg_t *self)
{
    if (NULL == self) {
        return 0;
    }

    return self->flags;
}


/**
 * Return message data property
 */
byte *zmtp_msg_data(zmtp_msg_t *self)
{
    if (NULL == self) {
        return NULL;
    }

    return self->data;
}

/**
 * Return message size property
 */
size_t zmtp_msg_size(zmtp_msg_t *self)
{
    if (NULL == self) {
        return 0;
    }

    return self->size;
}

#ifdef __SELF_TEST__

// --------------------------------------------------------------------------
// Selftest

void zmtp_msg_test(bool verbose)
{
    printf(" * zmtp_msg: ");
    // @selftest
    zmtp_msg_t *msg = zmtp_msg_from_const_data(0, "hello", 6);
    assert (msg);
    assert (zmtp_msg_flags(msg) == 0);
    assert (zmtp_msg_size(msg) == 6);
    assert (memcmp(zmtp_msg_data(msg), "hello", 6) == 0);
    zmtp_msg_destroy(&msg);
    assert (msg == NULL);
    // @end
    printf("OK\n");
}
#endif
